﻿#include "FITKAcousticsVTKGraphObjectSEABeam.h"
#include "FITKAcousticsVTKGraphActor.h"

// VTK
#include <vtkProperty.h>
#include <vtkUnstructuredGrid.h>
#include <vtkPoints.h>
#include <vtkCellData.h>
#include <vtkMapper.h>

// Data
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsSEAModel.h"
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsDiscNodeList.h"
#include "FITK_Component/FITKAcousticsModel/FITKSEASubSysBeam.h"

// Graph and filter
#include "FITKAcousticsVTKCommons.h"
#include "FITKAcousticsVTKSharedDataManager.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"

namespace Exchange
{
    FITKAcousticsVTKGraphObjectSEABeam::FITKAcousticsVTKGraphObjectSEABeam(Acoustics::FITKSEASubSysBeam* beam)
        : FITKAcousticsVTKGraphObjectSEASubSystem(beam)
    {
        // Return if the input is nullptr.
        if (!beam)
        {
            return;
        }

        // Intiailize the polygon offset.
        m_polygonOffset = FITKAcousticsVTKCommons::s_polygonOffsetSEABeam;

        // Initialize vtk objects.
        init();
        
        // Generate the data set.
        if (!generateDataSet(beam))
        {
            return;
        }

        // Generate the nodes actor.
        generateGraph();

        // Update the visibility.
        updateVisibility();

        // Save the data type.
        m_graphInfo.Type = FITKAcousticsVTKCommons::BeamGraph;
    }

    void FITKAcousticsVTKGraphObjectSEABeam::updateVisibility()
    {
        // Get the runtime settings for visibility.
        bool showBeam = getRuntimeSettingAs<bool>(RTS_KEY_VISIBLE_SEA_SUBSYS_BEAM, true);
        bool mainFlag = this->Superclass::getDataVisibility() && showBeam;

        // Update the visibility with the flags.
        //@{
        // Show or hide actors.
        setVisible(mainFlag);
        //@}
    }

    void FITKAcousticsVTKGraphObjectSEABeam::setEnableScale(bool isOn)
    {
        Q_UNUSED(isOn);
        return;
    }

    int FITKAcousticsVTKGraphObjectSEABeam::getShapeIdByVTKCellId(int vtkId, FITKAcousticsVTKCommons::ShapeMeshAbsEnum topAbsShapeType)
    {
        if (!m_ugrid)
        {
            return -1;
        }

        vtkCellData* cellData = m_ugrid->GetCellData();
        if (!cellData)
        {
            return -1;
        }

        switch (topAbsShapeType)
        {
        case Exchange::FITKAcousticsVTKCommons::STA_WIRE:
        case Exchange::FITKAcousticsVTKCommons::STA_SHAPE:
        {
            // Return the data object id.
            return _dataObj != nullptr ? _dataObj->getDataObjectID() : -1;
        }
        case Exchange::FITKAcousticsVTKCommons::STA_EDGE:
        {
            // Get the segment id array.
            vtkIntArray* idArray = vtkIntArray::SafeDownCast(cellData->GetArray(DATA_SUB_ID_ARRAY_NAME));
            if (!idArray)
            {
                return -1;
            }

            int nVal = idArray->GetNumberOfValues();
            if (vtkId >= nVal)
            {
                return -1;
            }

            // Get the sement index.
            int dataId = idArray->GetValue(vtkId);

            return dataId;
        }
        default:
            break;
        }

        return -1;
    }

    bool FITKAcousticsVTKGraphObjectSEABeam::getDataVisibility()
    {
        // Get the runtime settings for visibility.
        bool showBeam = getRuntimeSettingAs<bool>(RTS_KEY_VISIBLE_SEA_SUBSYS_BEAM, true);
        return this->Superclass::getDataVisibility() && showBeam;
    }

    bool FITKAcousticsVTKGraphObjectSEABeam::generateDataSet(Acoustics::FITKAbstractSEASubSys* subSys)
    {
        Acoustics::FITKSEASubSysBeam* beam = dynamic_cast<Acoustics::FITKSEASubSysBeam*>(subSys);
        if (!beam || !m_points || !m_ugrid)
        {
            return false;
        }

        // Get the SEA nodes from the SEA model.
        Acoustics::FITKAcousticsDiscNodeList* seaNodes = getSEANodesData();
        if (!seaNodes)
        {
            return false;
        }

        // Get the beam nodes.
        QList<int> ids = beam->getNodeIDs();

        // Remove the last direction node.
        ids.removeLast();

        // Old version.
        //@{
        //vtkSmartPointer<vtkIdList> polyLineCell = vtkSmartPointer<vtkIdList>::New();
        //double pos3[3]{ 0., 0., 0. };
        //for (const int & nId : ids)
        //{
        //    Core::FITKNode* node = seaNodes->getNodeByID(nId);
        //    if (!node)
        //    {
        //        continue;
        //    }

        //    node->getCoor(pos3);

        //    // Insert the node and insert a point to the poly line.
        //    int iPt = m_points->InsertNextPoint(pos3);
        //    polyLineCell->InsertNextId(iPt);
        //}

        //// Add the poly line to the grid.
        //m_ugrid->InsertNextCell(VTKCellType::VTK_POLY_LINE, polyLineCell);
        //@}

        // New version.
        //@{
        // The sub id array for the beam segent index.
        vtkSmartPointer<vtkIntArray> subIdArray = vtkSmartPointer<vtkIntArray>::New();
        subIdArray->SetName(DATA_SUB_ID_ARRAY_NAME);

        vtkCellData* cellData = m_ugrid->GetCellData();
        if (cellData)
        {
            cellData->AddArray(subIdArray);
        }

        // Add points.
        double pos3[3]{ 0., 0., 0. };
        for (const int & nId : ids)
        {
            bool flag = FITKAcousticsVTKCommons::GetSEANodePositionById(seaNodes, nId, pos3);
            Q_UNUSED(flag);

            // Insert the node and insert a point to the poly line.
            m_points->InsertNextPoint(pos3);
        }

        // Add cells and sub-id data.
        for (int i = 0; i < ids.count() - 1; i++)
        {
            vtkSmartPointer<vtkIdList> lineCell = vtkSmartPointer<vtkIdList>::New();
            lineCell->InsertNextId(i);
            lineCell->InsertNextId(i + 1);
            m_ugrid->InsertNextCell(VTKCellType::VTK_LINE, lineCell);

            subIdArray->InsertNextValue(i);
        }
        //@}

        return true;
    }

    void FITKAcousticsVTKGraphObjectSEABeam::generateGraph()
    {
        // Generate the actor.
        this->Superclass::generateGraph();

        m_fActor->setActorType(ActorType::EdgeActor);
        m_fActor->GetProperty()->SetEdgeVisibility(true);
        m_fActor->GetProperty()->SetLineWidth(3);

        // Initialize color.
        double color3[3]{ 0., 0., 0. };
        FITKAcousticsVTKCommons::QColorToDouble3(FITKAcousticsVTKCommons::s_SEASubSysBeamColor, color3);
        m_fActor->GetProperty()->SetColor(color3);
    }
}   // namespace Exchange